refactor(api): GenerationScheme protocol + registry [LTV-Pd]#107
Merged
Conversation
First step of the peer-schemes architecture (LTV-M2). Introduces the
generation-scheme abstraction and routes Generator.generate() through it,
wrapping the existing lead-scoring pipeline as the first registered scheme.
Lead-scoring output is unchanged (the wrapper delegates to the identical
functions); this PR is a pure refactor.
- leadforge/schemes/base.py — GenerationScheme protocol, SCHEME_REGISTRY,
register_scheme/get_scheme/available_schemes, UnknownSchemeError. The
protocol covers the generation half (build_population + simulate) wired
through Generator; render dispatch is added later (LTV-M6).
- leadforge/schemes/lead_scoring/__init__.py — LeadScoringScheme, delegating
to leadforge.simulation.{population,engine} (relocated under this package in
LTV-Pe); self-registers on import.
- leadforge/schemes/__init__.py — imports built-in schemes for their
registration side effect; re-exports the registry API.
- Recipe gains a `scheme` field (default "lead_scoring"); parsed in from_dict.
b2b_saas_procurement_v1/recipe.yaml declares `scheme: lead_scoring`.
- WorldSpec gains `scheme` (default "lead_scoring"); from_recipe threads
recipe.scheme through; Generator.generate() resolves and runs the scheme via
get_scheme() instead of calling build_population/simulate_world directly.
World-graph sampling and bundle rendering still run in their current paths
(lead-scoring only registered); they move under the scheme abstraction in
later M2/M6 PRs.
Tests: tests/schemes/test_registry.py (15) — registry resolution, protocol
conformance, conflict/unknown handling, Recipe.scheme parsing/validation,
WorldSpec default, Generator threading + end-to-end dispatch. Full suite 1495
passed / 51 skipped; ruff + mypy clean (87 files).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Check off LTV-Pd, link PR #107, note the byte-identical verification, and advance the agent-plan status to LTV-Pe next. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This comment has been minimized.
This comment has been minimized.
…V-Pd] Acts on a hostile self-review of the first revision. The original cut the abstraction at build_population/simulate with lead-scoring-shaped signatures (world_graph, category_latent_correlations, latent_touch_intensity, PopulationResult/SimulationResult), leaving graph sampling, difficulty interpretation, and bundle assembly hardcoded in Generator.generate(). That seam could not accommodate a non-lead-scoring scheme — defeating the point of extracting it early. Changes: - GenerationScheme protocol is now a single `build_world(config, narrative, **options) -> WorldBundle`. No lead-scoring types leak into the contract; scheme-specific flags ride through **options. - LeadScoringScheme.build_world owns the whole lead-scoring pipeline: hidden-DAG sampling, difficulty-profile → DifficultyParams + category-latent correlations (extracted to _resolve_difficulty), population, simulation, and WorldBundle assembly. Generator.generate() is now scheme-agnostic (override resolution + dispatch only). - Fix dropped-scheme bug: build_world sets spec.scheme=self.name, so bundle.spec.scheme reflects the actual scheme (was always the default). - DEFAULT_SCHEME constant in core.models removes the duplicated "lead_scoring" literal across Recipe/WorldSpec; a test guards LeadScoringScheme.name == DEFAULT_SCHEME. - Tests: regression for bundle.spec.scheme, determinism through the scheme path, name/default drift guard; protocol-conformance test annotated as the weak structural check it is. Verified still byte-identical to main (14/14 files of a pinned-timestamp bundle). Full suite 1498 passed / 51 skipped; ruff + mypy clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
pr-agent-context report: No unresolved review comments, failing checks, or actionable patch coverage gaps were found on PR #107 in repository https://github.com/leadforge-dev/leadforge. Treat this PR as all clear unless new signals appear.Run metadata: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First step of the peer-schemes architecture (
LTV-Pd, milestoneLTV-M2). Introduces the generation-scheme abstraction and routesGenerator.generate()through it, wrapping the existing lead-scoring pipelineas the first registered scheme. Pure refactor — lead-scoring output is
byte-identical (verified below).
What's added
leadforge/schemes/base.py—GenerationSchemeprotocol,SCHEME_REGISTRY,register_scheme/get_scheme/available_schemes,UnknownSchemeError. The protocol covers the generation half(
build_population+simulate) wired throughGenerator; render dispatchis added later (
LTV-M6).leadforge/schemes/lead_scoring/__init__.py—LeadScoringScheme,delegating to
leadforge.simulation.{population,engine}(those modulesrelocate under this package in
LTV-Pe); self-registers on import.leadforge/schemes/__init__.py— imports built-in schemes for theirregistration side effect; re-exports the registry API.
Recipegains aschemefield (default"lead_scoring"), parsed infrom_dict;b2b_saas_procurement_v1/recipe.yamldeclares it explicitly.WorldSpecgainsscheme(default"lead_scoring");from_recipethreadsrecipe.scheme;Generator.generate()resolves and runs the scheme viaget_scheme()instead of callingbuild_population/simulate_worlddirectly.World-graph sampling and bundle rendering stay in their current paths (only
lead_scoringis registered and run); they move under the scheme abstractionin later
LTV-M2/LTV-M6PRs.Byte-identical verification
Generated a pinned-timestamp
student_publicbundle (seed 42, intro,80/160/200) on
mainand on this branch and compared SHA-256 of every file:Tests
tests/schemes/test_registry.py(15): registry resolution,runtime_checkableprotocol conformance, conflict/unknown handling,
Recipe.schemeparsing/validation,
WorldSpecdefault,Generatorthreading + end-to-enddispatch (incl. unknown-scheme error path).
ruff check+ruff format --checkclean;mypy leadforge/clean (87 files).BUNDLE_SCHEMA_VERSIONunchanged.🤖 Generated with Claude Code